﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Permissions;
using Debug = UnityEngine.Debug;

namespace AIModel
{
    /// <summary>
    /// 每个房间进入游戏时，此类会根据地图尺寸生成2D寻路网格信息
    /// </summary>
    public class PathFindManager
    {
        #region  单例

        private static PathFindManager instance;


        private PathFindManager()
        {

        }

        public static PathFindManager GetInstance()
        {
            if (instance == null)
            {
                instance = new PathFindManager();
            }

            return instance;
        }

        #endregion

        public List<PathNode> OpenList = new List<PathNode>();
        public List<PathNode> CloseList = new List<PathNode>();
        public int Height;
        public int Width;
        public PathNode[,] MapData = null;

        /// <summary>
        /// 初始化节点
        /// </summary>
        /// <param name="Height">高度</param>
        /// <param name="Width">宽度</param>
        public void InitNewMap(int Height, int Width)
        {
            this.Width = Width;
            this.Height = Height;
            MapData = new PathNode[Width, Height];
            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    //todo 初始化时可设定边缘点的可通过宽度值
                    PathNode node = new PathNode(i, j);
                    node.NodeWidth = 1;
                    MapData[i, j] = node;
                }
            }
            Debug.Log("地图初始化成功");
        }

        /// <summary>
        /// 查找最佳路径
        /// </summary>
        /// <param name="_startX">起点X坐标</param>
        /// <param name="_startY">起点Y坐标</param>
        /// <param name="_endX">终点X坐标</param>
        /// <param name="_endY">终点Y坐标</param>
        /// <param name="NodeWidth">对象通过节点的宽度限制</param>
        /// <returns></returns>
        public List<PathNode> FindPath(int _startX, int _startY, int _endX, int _endY, int NodeWidth)
        {
            List<PathNode> result = new List<PathNode>();
            OpenList.Clear();
            CloseList.Clear();
            PathNode startNode = MapData[_startX, _startY];
            PathNode endNode = MapData[_endX, _endY];
            OpenList.Add(startNode);
            bool IsGetToEndNode = false;
            PathNode currentNode = null;
            //todo 此处可能会造成大量性能消耗，必要时开线程处理
            while (OpenList.Count > 0)
            {
                currentNode = OpenList[0];
                OpenList.Remove(currentNode);
                CloseList.Add(currentNode);
                //此处判断该点是否为结束点，如果不是，继续查找，如果是的，结束查找
                if (currentNode != endNode)
                {
                    //没有找到终点,收集周围存在的节点
                    GetAroundValidNode(currentNode, endNode);
                }
                else
                {
                    IsGetToEndNode = true;
                    break;
                }
            }

            //如果没找到，取出最近点
            if (!IsGetToEndNode)
            {
                currentNode = findNearPointFromList(CloseList, endNode);
                result.Add(currentNode);
            }

            if (currentNode == null)
            {
                return null;
            }

            //循环查找父对象并保存下来，这个列表即为最佳路径
            while (true)
            {
                result.Add(currentNode);

                if (currentNode == startNode)
                {
                    break;
                }

                currentNode = currentNode.ParentNode;
            }
            //反转列表，从0至最后为顺序路径
            result.Reverse();
            return result;
        }

        /// <summary>
        /// 取周围可用点
        /// </summary>
        public void GetAroundValidNode(PathNode node, PathNode _endNode)
        {
            //周围六个点，如果存在，直接添加到列表中
            CheckInMapForPath(node, node.PosX - 1, node.PosY - 1, _endNode);
            CheckInMapForPath(node, node.PosX - 1, node.PosY, _endNode);
            CheckInMapForPath(node, node.PosX - 1, node.PosY + 1, _endNode);
            CheckInMapForPath(node, node.PosX, node.PosY - 1, _endNode);
            CheckInMapForPath(node, node.PosX, node.PosY + 1, _endNode);
            CheckInMapForPath(node, node.PosX + 1, node.PosY - 1, _endNode);
            CheckInMapForPath(node, node.PosX + 1, node.PosY, _endNode);
            CheckInMapForPath(node, node.PosX + 1, node.PosY + 1, _endNode);
            OpenList = OpenList.OrderBy(item => item.AllCost).ToList();
        }

        /// <summary>
        /// 检测是否可行，可行则直接从mapdata内去除该对象
        /// </summary>
        /// <returns></returns>
        public void CheckInMapForPath(PathNode parentNode, int PosX, int PosY, PathNode _endNode)
        {
            //如果该点存在，并且该点不在关闭列表内，则设置其父节点，计算损耗，并添加在下一组新增列表中
            if (IsValid(PosX, PosY) && !IsInCloseList(MapData[PosX, PosY]))
            {
                //设置父节点
                MapData[PosX, PosY].ParentNode = parentNode;

                //todo 此处针对宽度条件,标签条件筛选节点

                //计算损耗 并添加到openlist里面
                CalculateCost(MapData[PosX, PosY], _endNode);
            }
        }

        /// <summary>
        /// 计算并保存消耗记录
        /// </summary>
        public void CalculateCost(PathNode _tempNode, PathNode _endNode)
        {
            //计算父物体到本物体的消耗，斜线计算为14，直线则为10
            if (Math.Abs(_tempNode.ParentNode.PosX - _tempNode.PosX) == 1 && Math.Abs(_tempNode.ParentNode.PosY - _tempNode.PosY) == 1)
            {
                _tempNode.FromParentCost = 14;
            }
            else
            {
                _tempNode.FromParentCost = 10;
            }

            //计算本物体到终点的消耗
            _tempNode.ToTargetCost = Math.Abs(_endNode.PosX - _tempNode.PosX) * 10 + Math.Abs(_endNode.PosY - _tempNode.PosY) * 10;
            //将最新的节点添加到可通过节点中
            OpenList.Add(_tempNode);
        }

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="PosX"></param>
        /// <param name="PosY"></param>
        /// <returns></returns>
        public bool IsValid(int PosX, int PosY)
        {
            if (PosX < 0 || PosX >= Width) return false;
            if (PosY < 0 || PosY >= Height) return false;
            return true;
        }

        /// <summary>
        /// 该点是否已经检测过
        /// </summary>
        /// <returns></returns>
        public bool IsInCloseList(PathNode node)
        {
            return CloseList.Contains(node);
        }

        /// <summary>
        /// 如果没有路径能到达终点，查找最近点
        /// </summary>
        /// <param name="list">close列表</param>
        /// <returns></returns>
        private PathNode findNearPointFromList(List<PathNode> list, PathNode endNode)
        {
            PathNode result = null;
            int minDistance = int.MaxValue;

            foreach (PathNode node in list)
            {
                int dist = (int)Math.Sqrt(Math.Abs(node.PosX - endNode.PosX) * Math.Abs(node.PosX - endNode.PosX) + Math.Abs(node.PosY - endNode.PosY) * Math.Abs(node.PosY - endNode.PosY));

                if (dist < minDistance)
                {
                    minDistance = dist;
                    result = node;
                }
            }

            return result;
        }
    }
}